
#include <stdio.h>
#include <apr_time.h>
#include "RateControl.h"

/** Simple rate control constructor.
 * 
 * @param ibytes_per_second
 * Maximum bytes per second.
 * 
 * @param icongestion_window_size
 * Isn't a congestion window size here, but in further classes this
 * is the window size shared between many packets-in-flight services
 * for optimising bandwidth/packet loss control. */
RateControl::RateControl(int64_t ibytes_per_second, double icongestion_window_size)
{
   bytes_per_second = ibytes_per_second;
   congestion_window_size = icongestion_window_size;
}

/** Blocks until the amount of the data specified can be sent.
 * 
 * @param datasize
 * The size of the data packet to be sent next. */
void RateControl::blockUntilAllowed(int datasize)
{
   int64_t difftime, bps, timetowait;
   int totalsize_in_collection = 0;
   // get total data in queue
   for (std::deque<std::pair<int64_t, int> >::iterator
      i = data_collection.begin(); i != data_collection.end(); i++)
   {
      totalsize_in_collection += i->second;
   }
   int64_t now_for_all_intents = apr_time_now();
   if (bytes_per_second < 0) goto finish;
   if (data_collection.empty()) goto finish;
   if (data_collection.back().first > now_for_all_intents)
   {
      // if the date rolls over while this is running, reset.
      // ha ha ha
      data_collection.clear();
      goto finish;
   }
   // difference between now and the end of the queue
   difftime = now_for_all_intents - data_collection.front().first;
   printf("difftime: %ld\n", difftime);
   // bytes per second at the moment...
   bps = (APR_USEC_PER_SEC * totalsize_in_collection) / ((double)difftime);
   printf("bps: %ld\n", bps);
   // determine amount of sleep required
   timetowait = ((totalsize_in_collection + datasize) / ((double)bytes_per_second)) * APR_USEC_PER_SEC;
   printf("timetowait: (%ld - %ld) usecs\n", timetowait, difftime);
   if (timetowait > difftime)
      apr_sleep(timetowait - difftime);
finish:
   if (data_collection.size() >= (int)congestion_window_size) data_collection.pop_front();
   data_collection.push_back(std::pair<int64_t, int>(apr_time_now(), datasize));
}

/** Prepare to send the packet (apply rate controls).
 * 
 * @param packet_id
 * Packet ID for tracking possible acknowledgements.
 * 
 * @param datasize
 * Size of the data packet.
 * 
 * @return
 * Zero. Other implementation overload this to specify
 * whether the destination needs an acknowledgement. */
int RateControl::prepareForSend(int32_t packet_id, int datasize)
{
   blockUntilAllowed(datasize);
   return 0;
}
